#include "coroutines/UE5Coro.h"
#include <iostream>
#include <chrono>

namespace UE5Coro {
// Awaiter
// template<>
// void LatentAwaiter::Suspend<LatentPromise>(LatentPromise& Promise) {
// };
void LatentAwaiter::Suspend(LatentPromise& Promise) noexcept {
    LatentActionManager::Get()->AddLatentAction(new LatentAction(*this, &Promise));
}

// Promise
FInitialSuspendAwaiter LatentPromise::initial_suspend() {
    // 手动管理当前initial是什么状态

    // 手动管理当前initial是什么状态
    return {UE5Coro::FInitialSuspendAwaiter::EAction::Resume};
}
FFinalSuspendAwaiter LatentPromise::final_suspend() noexcept {
    return {false}; // 正式的coro中，这里是true，因为那边主线程不是跑的死循环而是tick
}

// LatentFunction
double GetTimeFunc() {
    auto now = std::chrono::system_clock::now();
    auto duration_since_epoch = now.time_since_epoch();
    std::chrono::duration<double> seconds_with_fraction = duration_since_epoch;
    return seconds_with_fraction.count();
}
template<auto GetTime>
bool WaitUntilTime(void* State, bool /*bCleanup*/) {
    auto& TargetTime = reinterpret_cast<double&>(State);
    return GetTime() >= TargetTime;
}
template<auto GetTime, bool bTimeIsOffset>
LatentAwaiter GenericUntil(double Time) {
    if constexpr (bTimeIsOffset) { Time += GetTime(); }
    void* State = nullptr;
    reinterpret_cast<double&>(State) = Time;
    return LatentAwaiter(State, WaitUntilTime<GetTime>);
}
LatentAwaiter DelaySeconds(double Seconds) {
    return GenericUntil<&GetTimeFunc, true>(Seconds);
}

// 首先看返回类型里面有没有promise_type
//     可以直接在类里面使用using设置
//     可以通过coroutine_traits设置
// 再检查promise_type是否符合条件
//     
VoidCoroutine CoroutineFunc() {
    static const double DelayTime = 2.0;
    
    std::cout << "Start Coroutine: " << DelayTime << "s" << std::endl;
    co_await DelaySeconds(DelayTime);
    std::cout << "End Coroutine" << std::endl;
}
void Test() {
    auto Coroutine = CoroutineFunc();

    while (true) {
        if (Coroutine.Handle.done()) {
            break;
        }
        LatentActionManager::Get()->UpdateOperations();
    }
    Coroutine.Handle.destroy();
}

// UE5Coro
// 定义了FVoidCoroutine返回值的函数作为协程的返回值
//     其中，Coroutine通过coroutine_traits和Promise绑定
// 创建协程的时候，按照如下顺序调用函数
//     0. FLatentPromise构造函数
//          1. 创建LatentAction FPendingLatentCoroutine         这个LatentAction作为后面异步Awaiter的插槽存在，也作为Resume轮询的基础
//     1. FLatentPromise get_return_object          返回VoidCoroutine
//     2. FLatentPromise initial_suspend            根据当前的条件初始化Awaiter
//     3. FInitialSuspend await_ready               通常是false，直接挂起
//     4. FInitialSuspend await_suspend             根据Promise初始化结果决定是否执行
//     5. FInitialSuspend await_resume          
// 协程运行中，需要调用别的异步函数，此时会创建一个新的Awaiter
//     1. FLatentAwaiter await_ready                根据传入的数据State，与传入的判断函数Resume，决定是否需要继续挂起
//     2. FLatentAwaiter await_suspend              将挂起这个信息传递给FLatentPromise
//          1. FLatentAwaiter Suspend FLatentPromise&
//          2. FLatentPromise SetCurrentAwaiter                 设置当前的Awaiter
//          3. FPendingLatentCoroutine SetCurrentAwaiter        将当前的Awaiter设置到LatentAction插槽中
//          4. FPendingLatentCoroutine UpdateOperation          通过UE原生的LatentAction系统轮询，来检测当前的Awaiter是否执行完毕了
//     3. FLatentAwaiter await_resume               上面检测Awaiter执行完毕之后，回来继续Resume

// Promise相当于这个协程函数体
// Awaiter相当于协程函数体中异步函数委托

// Awaiter ->|Suspend| Promise -> Action
// System ->|tick| Action
}
